/** @file    Door.cpp
 *  @author  Alessandro Polo
 *  @version $Id: Door.cpp 2882 2010-08-09 11:08:30Z alex $
 *  @brief
 * File containing methods for the wosh::building::Door class.
 * The header for this class can be found in Door.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <framework/building/Door.h>

 #include <core/ObjectAllocator.h>
 #include <core/MethodsCommon.h>
 #include <core/List.h>


namespace wosh {
 namespace building {

 WOSH_REGISTER(wosh::building::Door, "wosh::building::BuildingObject", _Door_VERSION, _static_Door )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

Door::Door()
	: BuildingObject() {
	BuildingObject::setName( "wosh.building.Door", _GET_Abs(Utilities::randomLong()) );
 }

void Door::init() {
	Log(LOG_DEBUG, " Setting default properties and permissions.." );
	Properties.set( _Door_KEY_ExternalDoor, false, Permission(Permission::RW, Permission::RW, Permission::Read) );
	Properties.set( _Door_KEY_AccessTo, "", Permission(Permission::RW, Permission::RW, Permission::Read) );

	this->state = Door::DOOR_UNKNOWN;
	setState(Door::DOOR_UNKNOWN, false);

	Log(LOG_DEBUG, " Registering methods.." );
	MethodWRESULTwSecurityToken* mmOpen = Methods.create<MethodWRESULTwSecurityToken>( _Door_METHOD_open, "open door", Permission::R_XR_XR__() );
	mmOpen->setMethod( this, (MethodWRESULTwSecurityTokenPtr)&Door::mmDoOpen );

	MethodWRESULTwSecurityToken* mmClose = Methods.create<MethodWRESULTwSecurityToken>( _Door_METHOD_close, "close door", Permission::R_XR_XR__() );
	mmClose->setMethod( this, (MethodWRESULTwSecurityTokenPtr)&Door::mmDoClose );

	MethodWRESULTwSecurityToken* mmLock = Methods.create<MethodWRESULTwSecurityToken>( _Door_METHOD_lock, "lock door", Permission::R_XR_XR__() );
	mmLock->setMethod( this, (MethodWRESULTwSecurityTokenPtr)&Door::mmDoLock );

	MethodWRESULTwSecurityToken* mmUnlock = Methods.create<MethodWRESULTwSecurityToken>( _Door_METHOD_unlock, "unlock door", Permission::R_XR_XR__() );
	mmUnlock->setMethod( this, (MethodWRESULTwSecurityTokenPtr)&Door::mmDoUnlock );

	BuildingObject::init();
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////// PROCESS CHILD MESSAGES

void Door::busMessageChild( const Message& childMessage ) {
	if ( childMessage.isEmpty() ) return;
	if ( !childMessage.getContent()->isFact() ) return;
	const Fact* fact = childMessage.getContent()->asFact();
	if ( fact == NULL ) return;

	if ( fact->getEvent() == _Device_EVENT_StateChanged ) {
		if ( !fact->hasData() ) {
			Log(LOG_WARNING, ":busMessageChild() EVENT 'StateChanged' Invalid, no Content!" );
			return;
		 }
		const Variant* data = NULL;
		if ( fact->getData()->isKindOf<Variant>() )
			data = dynamic_cast<const Variant*>( fact->getData() );
		if ( data == NULL ) {
			Log(LOG_WARNING, ":busMessageChild() EVENT 'StateChanged' Invalid, Not Data-Content!" );
			return;
		 }
		int value = data->toInteger(-1);
		Log(LOG_INFO, ":busMessageChild() EVENT 'StateChanged' => %d [%s]", value, data->getTypeAsString() );

		if ( value == 0 )
			setState(Door::DOOR_CLOSED, true, childMessage.getSecurityToken());
		else if ( value == 100 )
			setState(Door::DOOR_OPENED, true, childMessage.getSecurityToken());
		else
			setState(Door::DOOR_UNKNOWN, true, childMessage.getSecurityToken());
	 }
	else
		Log(LOG_WARNING, ":busMessageChild() UNKNOWN EVENT '%s'", fact->getEvent().c_str() );
 }

////////////////////////////////////////////////////////////////////////////////////// PROCESS CHILD MESSAGES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////// GENERATED EVENTS

void Door::setState( Door::DOOR_STATE new_state, bool raise_event, const SecurityToken* source_token ) {
	Door::DOOR_STATE old_state = this->state;
	Properties.transactionBeginWrite();
	if ( this->state != new_state ) {
		this->state = new_state;
		Properties.update( _BuildingObject_KEY_State, getStateAsString() );
	 }
	Properties.update( _BuildingObject_KEY_StateTs, Variant(Utilities::std_time(), Variant::DATETIME) );
	Properties.transactionEnd();
	if ( this->raiseAlwaysEvents || raise_event ) {
		Log(LOG_VERBOSE, ":setState(%s) Raising Event.. ", getStateAsString() );
		BuildingObject::raiseEvent( new Door_StateChanged(this->state, old_state), source_token );
	 }
	else
		Log(LOG_DEBUG, ":setState(%s) Not Raising Event.", getStateAsString() );
 }

//////////////////////////////////////////////////////////////////////////////////////////// GENERATED EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// DEVICE METHODS

WRESULT Door::mmDoOpen( const SecurityToken* security ) {
	Log(LOG_INFO, ":mmDoOpen()" );
	// state update is asynch. raised by Child-Device (as Fact)
	Request* request = new Request( _Switch_METHOD_on );
	request->setOmitResponse(true);
	BuildingObject::controlChild( getMainSwitchName(), request, security );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT Door::mmDoClose( const SecurityToken* security ) {
	Log(LOG_INFO, ":mmDoClose()" );
	// state update is asynch. raised by Child-Device (as Fact)
	Request* request = new Request( _Switch_METHOD_off );
	request->setOmitResponse(true);
	BuildingObject::controlChild( getMainSwitchName(), request, security );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT Door::mmDoLock( const SecurityToken* security ) {
	Log(LOG_INFO, ":mmDoLock()" );
	// state update is asynch. raised by Child-Device (as Fact)
	Request* request = new Request( _Switch_METHOD_on );
	request->setOmitResponse(true);
	BuildingObject::controlChild( wosh::interfaces::devices::Switch::bundleType(), request, security );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT Door::mmDoUnlock( const SecurityToken* security ) {
	Log(LOG_INFO, ":mmDoUnlock()" );
	// state update is asynch. raised by Child-Device (as Fact)
	Request* request = new Request( _Switch_METHOD_off );
	request->setOmitResponse(true);
	BuildingObject::controlChild( wosh::interfaces::devices::Switch::bundleType(), request, security );
	return WRET_OK;
 }

////////////////////////////////////////////////////////////////////////////////////////////// DEVICE METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool Door::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = BuildingObject::updateHash( hash_context );
	ret = hash_context->update( this->state ) & ret;
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

const char* DoorStateStr[] = {
	"UNKNOWN",	// (DOOR_STATE)0
	"OPENED",	// (DOOR_STATE)1
	"CLOSED",	// (DOOR_STATE)2
	"LOCKED"	// (DOOR_STATE)3
 };

Enum2StrFromArray(Door::getStateAsString, Door::DOOR_STATE, DOOR_OPENED, DOOR_LOCKED, 0, DoorStateStr)

Str2EnumByArray(Door::getStateFromString, Door::DOOR_STATE, 0, DOOR_LOCKED, DOOR_UNKNOWN, DoorStateStr)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Door::DOOR_STATE intToDoorStateEnum( int value ) {
	if ( value <= Door::DOOR_UNKNOWN ) return Door::DOOR_UNKNOWN;
	if ( value > Door::DOOR_LOCKED ) return Door::DOOR_UNKNOWN;
	return (Door::DOOR_STATE)value;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::building::Door_StateChanged, "wosh::Fact", 1.0, _static_Door_StateChanged )

Door_StateChanged::Door_StateChanged( Door::DOOR_STATE current_door_state )
	: wosh::Fact( _Door_EVENT_DoorStateChanged ) {
	setData( new Variant(current_door_state) );
 }

Door_StateChanged::Door_StateChanged( Door::DOOR_STATE current_door_state, Door::DOOR_STATE previous_door_state )
	: wosh::Fact( _Door_EVENT_DoorStateChanged ) {
	List* args = List::createFrom<Variant>( (int)current_door_state, (int)previous_door_state );
	setData(args);
 }

Door::DOOR_STATE Door_StateChanged::getDoorState() const {
	const Variant* var = getDataItem<Variant>(0);
	if ( var == NULL ) return Door::DOOR_UNKNOWN;
	if ( var->isNumeric() ) return Door::DOOR_UNKNOWN;
	return intToDoorStateEnum(var->toInteger( (int)Door::DOOR_UNKNOWN ));
 }

Door::DOOR_STATE Door_StateChanged::getPreviousDoorState() const {
	const Variant* var = getDataItem<Variant>(1);
	if ( var == NULL ) return Door::DOOR_UNKNOWN;
	if ( var->isNumeric() ) return Door::DOOR_UNKNOWN;
	return intToDoorStateEnum(var->toInteger( (int)Door::DOOR_UNKNOWN ));
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////




 }; // namespace building
}; // namespace wosh
